GtkWidget *main_window = NULL;
GtkWidget *statusbar = NULL;
GtkWidget *entry = NULL;
GtkWidget *switcher = NULL;
GtkWidget *notebook = NULL;
GtkWidget *treeview = NULL;

/**
** @fn clear_message
** @brief callback function to clear the buffer in a textview
** @param button is a button pointer to get click event
** @param textview is a pointer to get the buffer from textview and clear it
*/

void clear_message(GtkButton *button, Chat *chat) {
	gtk_text_buffer_set_text((gtk_text_view_get_buffer(GTK_TEXT_VIEW(chat->widgets[TEXTVIEW_1]))), "", -1);
}

/**
** @fn send_message
** @brief callback function to send some message when the button is clicked
** @param button is a button pointer to get click event
** @param textview is a pointer to get the message from buffer of the textview and send it
*/

void send_message(GtkButton *button, Chat *chat) {
	IFProtMessage msg;
	GtkTextIter start;
	GtkTextIter end;
	GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(chat->widgets[TEXTVIEW_1]));	
	gtk_text_buffer_get_bounds(buffer, &start, &end);
	gchar *content = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
	create_chat_message(&msg, PORT_STD, content);
	if(send(chat->socket, &msg, sizeof msg, 0) != ERROR) {
                char str[USERNAME_CAPACITY + 1 + DATA_CAPACITY];
                sprintf(str, "%s: %s\n\n", username, content);          
                gtk_text_buffer_insert_at_cursor(gtk_text_view_get_buffer(GTK_TEXT_VIEW(chat->widgets[TEXTVIEW_0])), str, strlen(str));
                clear_message(NULL, chat);
                sprintf(str, "%d %s/%d %s", ++chat->counter_posts, POSTS, chat->counter_recvs, MESSAGES_RECEIVED);              
                update_status_bar(str);
        }
        else update_status_bar(SEND_ERROR);

}

/**
** @fn close_chat
** @brief callback function to close some page from the notebook
** @param button is a button pointer to get click event
** @param chat is a pointer to reference the chat
*/

void close_chat(GtkButton *button, Chat *chat) {
	int i = 0;	
	gtk_notebook_remove_page(GTK_NOTEBOOK(notebook), chat->page);
	for(i = 0; i < g_slist_length(chats); ++i) {
		Chat *ch = g_slist_nth_data(chats, i);
		if(ch->page > chat->page)
			--ch->page;
	}	
	chat->page = -1;
	pthread_kill(chat->thread, 0);
	close(chat->socket);
	if(gtk_notebook_get_n_pages(GTK_NOTEBOOK(notebook)) == 0) {
		int w, h;
		gtk_window_get_size(GTK_WINDOW(main_window), &w, &h);
		gtk_window_resize(GTK_WINDOW(main_window), 200, h);
		gtk_widget_set_visible(notebook, FALSE);
	}
}

/**
** @fn open_chat
** @brief open some page on the notebook
** @param chat is a pointer of Chat
*/

void open_chat(Chat *chat) {
	create_chat_widgets(chat);
	chat->page = gtk_notebook_append_page(GTK_NOTEBOOK(notebook), chat->widgets[BOX_1], chat->widgets[BOX_0]);
	g_signal_connect(GTK_BUTTON(chat->widgets[BUTTON_0]), "clicked", G_CALLBACK(close_chat), chat);
	g_signal_connect(GTK_BUTTON(chat->widgets[BUTTON_1]), "clicked", G_CALLBACK(send_message), chat);
	g_signal_connect(GTK_BUTTON(chat->widgets[BUTTON_2]), "clicked", G_CALLBACK(clear_message), chat);
	gtk_widget_set_visible(notebook, TRUE);
}

/**
** @fn change_page
** @brief callback function to switch the page of a notebook
*/

void change_page(GtkNotebook *notebook, gpointer page, guint page_num, gpointer data) {
	int i, pn = gtk_notebook_get_current_page(notebook);
	for(i = 0; i < g_slist_length(chats); ++i) {
		Chat *ch = g_slist_nth_data(chats, i);
		if(ch->page == pn) {
			char str[32];
			sprintf(str, "%d %s/%d %s", ch->counter_posts, POSTS, ch->counter_recvs, MESSAGES_RECEIVED);
			update_status_bar(str);
		}
	}
}

/**
** @fn connect_chat
** @brief connects to server and opens a page on the notebook for a chat
** @param chat is a pointer of Chat
*/

void connect_chat(Chat *chat) {
	if(connect_server(chat) != ERROR) {
		open_chat(chat);
		pthread_create(&chat->thread, NULL, receive_chat_messages, (void *) chat);
	}
	else update_status_bar(CONNECT_ERROR);
}

/**
** @fn db_click_row
** @brief callback function to create page of notebook when a row from treeview is double-clicked
** @param treeview is a pointer of reference for a treeview
** @param path is a pointer of reference for a path of treeview
** @param col is a pointer of reference for a column of treeview
** @param userdata is a pointer of reference for the data of a column of a treeview
*/

void db_click_row(GtkTreeView *treeview, GtkTreePath *path, GtkTreeViewColumn  *col, gpointer userdata) {
	GtkTreeModel *model;
	GtkTreeIter it;
	model = gtk_tree_view_get_model(treeview);
	if(gtk_tree_model_get_iter(model, &it, path)) {
		strtok(gtk_tree_path_to_string(path), ":");
		int index = atoi(strtok(NULL, ":"));
		Chat *ch = g_slist_nth_data(chats, index);
		if(ch->page != -1)
			gtk_notebook_set_current_page(GTK_NOTEBOOK(notebook), ch->page);
		else connect_chat(ch);
	}
}

/**
** @fn create_model_view
** @brief creates a tree model for the treeview
** @param tv is a pointer of reference for the treeview
*/

void create_model_view( ) {
	GtkCellRenderer *renderer;
	GtkTreeModel *tm;
	GtkTreeStore *ts;
	GtkTreeViewColumn *col;
	ts = gtk_tree_store_new(1, G_TYPE_STRING);
	gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(treeview), 0);
	col = gtk_tree_view_column_new( );
	gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), col);
	renderer = gtk_cell_renderer_text_new( );
	gtk_tree_view_column_pack_start(col, renderer, TRUE);
	gtk_tree_view_column_add_attribute(col, renderer, "text", 0);
	tm = GTK_TREE_MODEL(ts);
	gtk_tree_view_set_model(GTK_TREE_VIEW(treeview), tm);
	g_object_unref(ts);
}

/**
** @fn init_tree_view
** @brief initializes treeview with a basic struct
** @param tv is a pointer of reference for the treeview
*/

void init_tree_view( ) {
	GtkTreeStore *ts = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(treeview)));
	GtkTreeIter it;
	gtk_tree_store_append(ts, &it, NULL);
	gtk_tree_store_set(ts, &it, 0, COL_GRP, 1,-1);
	gtk_tree_store_append(ts, &it, NULL);
	gtk_tree_store_set(ts, &it, 0, COL_USR, -1);
}

/**
** @fn update_tree_view_users
** @brief updates the treeview users content
** @param tv is a pointer of reference for the treeview
*/

void update_tree_view_users( ) {
	GtkTreeModel *tm = gtk_tree_view_get_model(GTK_TREE_VIEW(treeview));
	GtkTreePath *path = gtk_tree_path_new_from_string("1");
	GtkTreeIter it, child;
	int i, n_children;
	gtk_tree_model_get_iter(tm, &it, path);
	n_children = gtk_tree_model_iter_n_children(tm, &it);
	gtk_tree_path_free(path);
	path = gtk_tree_path_new_from_string("1:0");
	while(n_children > 0) {
		gtk_tree_model_get_iter(tm, &child, path);
		gtk_tree_store_remove(GTK_TREE_STORE(tm), &child);
		gtk_tree_path_free(path);
		path = gtk_tree_path_new_from_string("1:0");
		--n_children;
	}
	for(i = 0; i < g_slist_length(chats); ++i) {
		Chat *ch = g_slist_nth_data(chats, i);
		char address[INET_ADDRSTRLEN];
		char content[USERNAME_CAPACITY + 1 + INET_ADDRSTRLEN];
		inet_ntop(AF_INET, &ch->sck_addr.sin_addr, address, INET_ADDRSTRLEN);
		sprintf(content, "%s@%s", ch->username, address);
		gtk_tree_store_append(GTK_TREE_STORE(tm), &child, &it);
		gtk_tree_store_set(GTK_TREE_STORE(tm), &child, 0, content, -1);
	}
	gtk_tree_view_expand_all(GTK_TREE_VIEW(treeview));
}

/**
** @fn update_tree_view_users
** @brief updates the treeview users content
** @param tv is a pointer of reference for the treeview
*/

void update_tree_view_groups( ) {
	GtkTreeModel *tm = gtk_tree_view_get_model(GTK_TREE_VIEW(treeview));
	GtkTreePath *path = gtk_tree_path_new_from_string("0");
	GtkTreeIter it, child;
	int i, n_children;
	gtk_tree_model_get_iter(tm, &it, path);
	n_children = gtk_tree_model_iter_n_children(tm, &it);
	gtk_tree_path_free(path);
	path = gtk_tree_path_new_from_string("0:0");
	while(n_children > 0) {
		gtk_tree_model_get_iter(tm, &child, path);
		gtk_tree_store_remove(GTK_TREE_STORE(tm), &child);
		gtk_tree_path_free(path);
		path = gtk_tree_path_new_from_string("0:0");
		--n_children;
	}
	for(i = 0; i < g_slist_length(chats); ++i) {
		Chat *ch = g_slist_nth_data(chats, i);
		char address[INET_ADDRSTRLEN];
		char content[USERNAME_CAPACITY + 1 + INET_ADDRSTRLEN];
		inet_ntop(AF_INET, &ch->sck_addr.sin_addr, address, INET_ADDRSTRLEN);
		sprintf(content, "%s@%s", ch->username, address);
		gtk_tree_store_append(GTK_TREE_STORE(tm), &child, &it);
		gtk_tree_store_set(GTK_TREE_STORE(tm), &child, 0, content, -1);
	}
	gtk_tree_view_expand_all(GTK_TREE_VIEW(treeview));
}

/**
** @fn update_tree_view
** @brief updates the treeview content
** @param tv is a pointer of reference for the treeview
*/

void update_tree_view( ) {
	if(gtk_switch_get_active(GTK_SWITCH(switcher))) {
		update_tree_view_users( );
		
/*
		GtkTreeModel *tm = gtk_tree_view_get_model(GTK_TREE_VIEW(treeview));
		GtkTreePath *path = gtk_tree_path_new_from_string("1");
		GtkTreeIter it, child;
		int i, n_children;
		gtk_tree_model_get_iter(tm, &it, path);
		n_children = gtk_tree_model_iter_n_children(tm, &it);
		gtk_tree_path_free(path);
		path = gtk_tree_path_new_from_string("1:0");
		while(n_children > 0) {
			gtk_tree_model_get_iter(tm, &child, path);
			gtk_tree_store_remove(GTK_TREE_STORE(tm), &child);
			gtk_tree_path_free(path);
			path = gtk_tree_path_new_from_string("1:0");
			--n_children;
		}
		for(i = 0; i < g_slist_length(chats); ++i) {
			Chat *ch = g_slist_nth_data(chats, i);
			char address[INET_ADDRSTRLEN];
			char content[USERNAME_CAPACITY + 1 + INET_ADDRSTRLEN];
			inet_ntop(AF_INET, &ch->sck_addr.sin_addr, address, INET_ADDRSTRLEN);
			sprintf(content, "%s@%s", ch->username, address);
			gtk_tree_store_append(GTK_TREE_STORE(tm), &child, &it);
			gtk_tree_store_set(GTK_TREE_STORE(tm), &child, 0, content, -1);
		}
		gtk_tree_view_expand_all(GTK_TREE_VIEW(treeview));
*/
	}
}

/**
** @fn update_status_bar
** @brief updates the status bar content
** @param message is a string
*/

void update_status_bar(char *message) {
	gtk_statusbar_push(
		GTK_STATUSBAR(statusbar),
		gtk_statusbar_get_context_id(GTK_STATUSBAR(statusbar), ""),
		message
	);
}

/**
** @fn activate_program
** @brief callback function to activate or disactivate program
** @param button is a switcher pointer to get activate event
** @param GParamSpec is a list of params requested by the event
** @param entry is a pointer of reference for an entry
*/

void activate_program(GtkSwitch *switcher, GParamSpec *param, GtkEntry *entry) {
	strcpy(username, gtk_entry_get_text(entry));
	if(gtk_switch_get_active(switcher)) {
		gtk_widget_set_sensitive(GTK_WIDGET(entry), FALSE);
		init_tree_view( );
		start_network_services( );
		send_discovery_request_message( );
		gtk_tree_view_expand_all(GTK_TREE_VIEW(treeview));
	}
	else {
		int i;
		gtk_tree_store_clear(GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(treeview))));
		for(i = 0; i < g_slist_length(chats); ++i) {
			Chat *chat = g_slist_nth_data(chats, i);
			if(chat->page != -1)
				close_chat(NULL, chat);
		}		
		g_slist_free(chats);
		chats = NULL;
		update_status_bar(TYPE_NAME);
		gtk_widget_set_sensitive(GTK_WIDGET(entry), TRUE);
		stop_network_services( );
	}
}

/**
** @fn init_widgets
** @brief initializes the widgets of GUI
** @param builder is a pointer of reference for a builder
*/

void init_widgets(GtkBuilder *builder) {
	main_window = GTK_WIDGET(gtk_builder_get_object(builder, "main_window"));
	statusbar = GTK_WIDGET(gtk_builder_get_object(builder, "statusbar1"));
	switcher = GTK_WIDGET(gtk_builder_get_object(builder, "switch1"));
	entry = GTK_WIDGET(gtk_builder_get_object(builder, "entry1"));
	notebook = GTK_WIDGET(gtk_builder_get_object(builder, "notebook1"));
	treeview = GTK_WIDGET(gtk_builder_get_object(builder, "treeview1"));
	create_model_view( );
	update_status_bar(TYPE_NAME);
}

/**
** @fn end_gui
** @brief callback function to finalize GUI
*/

void end_gui( ) {
	gtk_main_quit( );
	end_network_services( );
	g_slist_free(chats);
}

/**
** @fn connect_signals
** @brief connects signal of the widgets of GUI
*/

void connect_signals( ) {
	g_signal_connect_swapped(G_OBJECT(main_window), "destroy", G_CALLBACK(end_gui), G_OBJECT(main_window));
	g_signal_connect(treeview, "row-activated", G_CALLBACK(db_click_row), NULL);
	g_signal_connect(GTK_SWITCH(switcher), "notify::active", G_CALLBACK(activate_program), GTK_ENTRY(entry));
	g_signal_connect(GTK_NOTEBOOK(notebook), "switch-page", G_CALLBACK(change_page), NULL);
}

/**
** @fn create_gui
** @brief interface function to create the GUI
*/

void create_gui(int *argc, char ***argv) {
	gtk_init(argc, argv);
	GtkBuilder *builder = gtk_builder_new( );
	gtk_builder_add_from_file (builder, "if-chat.xml", NULL);
	gtk_builder_connect_signals(builder, NULL);
	init_widgets(builder);
	connect_signals( );
	g_object_unref(G_OBJECT(builder));
	gtk_widget_show(main_window);
	gtk_main( );
}
